#include <winsock2.h>
#include <tchar.h>
#include <io.h>
#include <tchar.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <share.h>

//GDI+ support
#include <Gdiplus.h>

/* UELogger Support */
#include "litelog.h"

#include "UEService.h"
#include "uecgi.h"

/* Log File Name and Log Folder */
static const _TCHAR logfile[] = _T("UEServer.log");
static const _TCHAR logfolder[] = _T("../logs/");
extern "C" int logfd = 0;

/* UEService Instance */
CUEService theUEService;

static void display_usage() 
{
	_tprintf(_T("Command-line options:\n"));
	_tprintf(_T("  -start    : Start the Ultro Explorer server system service.\n"));
	_tprintf(_T("  -stop     : Stop the Ultro Explorer server system service.\n"));
	_tprintf(_T("  -initdb   : Create and initialize Ultro Explorer server database.\n"));
}

static int PrepareFileLogger(const _TCHAR *strLogFileName, const _TCHAR *strLogFolder = NULL)
{
	/* Create the log folder if not exists */
	if (strLogFolder && _tcslen(strLogFolder) > 0)
	{
		if (_taccess(strLogFolder, 0))
		{
			_tmkdir(strLogFolder);
		}
	}

	/* Open the log file and return the file descriptor */
	int fd = 0;
	_TCHAR strLogFileFullPath[MAX_PATH];
	_tfullpath(strLogFileFullPath, strLogFolder, MAX_PATH);
	if (!_tcscat_s(strLogFileFullPath, MAX_PATH, strLogFileName))
	{
		_tsopen_s(&fd, strLogFileFullPath, _O_APPEND | _O_CREAT | _O_WRONLY, _SH_DENYWR, _S_IREAD | _S_IWRITE);
	}

	return fd;
}

static BOOL WINAPI ControlHandler(DWORD fdwCtrlType) 
{
	switch( fdwCtrlType )
	{
	/* Handle CTRL+C event */
	case CTRL_C_EVENT:
		dbglog("Receive CTRL+C event, stop service", DEBUG_LOG_LEVEL_INFO, logfd, __FILE__, __LINE__);
		theUEService.Stop();
		return TRUE;
	/* Handle console window close event */
	case CTRL_CLOSE_EVENT:
		dbglog("Receive console window close event, stop service", DEBUG_LOG_LEVEL_INFO, logfd, __FILE__, __LINE__);
		theUEService.Stop();
		return TRUE;
	default:
		return FALSE;
	} // end of switch
} 


static int InitWinSock2()
{
	WORD wVersionRequested;
	WSADATA wsaData;

	wVersionRequested = MAKEWORD( 2, 2 );

	dbglog("Initialize windows socket", DEBUG_LOG_LEVEL_INFO, logfd, __FILE__, __LINE__);

	if ( WSAStartup( wVersionRequested, &wsaData ) != 0 ) {
		/* Tell the user that we could not find a usable */
		/* WinSock DLL.                                  */
		dbglog("Failed to initialize windows socket", DEBUG_LOG_LEVEL_ERRO, logfd, __FILE__, __LINE__);

		return -1;
	}
	 
	/* Confirm that the WinSock DLL supports 2.2.*/
	/* Note that if the DLL supports versions greater    */
	/* than 2.2 in addition to 2.2, it will still return */
	/* 2.2 in wVersion since that is the version we      */
	/* requested.                                        */
	if ( LOBYTE( wsaData.wVersion ) != 2 ||
			HIBYTE( wsaData.wVersion ) != 2 ) {
		/* Tell the user that we could not find a usable */
		/* WinSock DLL.                                  */
		dbglog("Incompatable windows socket version", DEBUG_LOG_LEVEL_ERRO, logfd, __FILE__, __LINE__);
		WSACleanup( );
		return -1; 
	}

	return 0;
}

int _tmain(int argc, _TCHAR** argv)
{
	_TCHAR strBaseDir[MAX_PATH];
	BOOL bDropOldDB = TRUE;
	int nRet = 0;

	/* Initialize debug log */
	logfd = PrepareFileLogger(logfile, logfolder);
#if defined(_DEBUG) || defined(DEBUG)
	loglevel = DEBUG_LOG_LEVEL_INFO;
#endif

	/* Set console handler to handle CTRL+C and console window close event */
	dbglog("Startup UEServer...", DEBUG_LOG_LEVEL_INFO, logfd, __FILE__, __LINE__);
	if (!SetConsoleCtrlHandler(ControlHandler, TRUE))
	{
		dbglogex("Failed to set the console handler. Error Num: %d", DEBUG_LOG_LEVEL_ERRO, logfd, __FILE__, __LINE__, GetLastError());
	}
	dbglog("Set console handler success.", DEBUG_LOG_LEVEL_INFO, logfd, __FILE__, __LINE__);

	/* Get basedir */
	if (GetModuleFileName(NULL, strBaseDir, MAX_PATH))
	{
		_TCHAR* pos = _tcsrchr(strBaseDir, _T('\\'));
		if (pos)
		{
			pos[0] = 0;
		}

		pos = _tcsrchr(strBaseDir, _T('\\'));
		if (pos)
		{
			pos[1] = 0;
		}
	}
	else
	{
		strBaseDir[0] = 0;
	}
	dbglogex("Basedir is: %s", DEBUG_LOG_LEVEL_INFO, logfd, __FILE__, __LINE__, strBaseDir);

	ULONG_PTR           gdiplusToken = NULL;
	Gdiplus::GdiplusStartupInput gdiplusStartupInput;

	// Initialize GDI+.
	nRet = Gdiplus::GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
	if (!nRet)
	{
		dbglog("Initialize GDI+ success", DEBUG_LOG_LEVEL_INFO, logfd, __FILE__, __LINE__);

		/* Initialize Windows Socket */
		nRet = InitWinSock2();
		if (!nRet)
		{
			dbglog("Initialize windows socket success", DEBUG_LOG_LEVEL_INFO, logfd, __FILE__, __LINE__);

			/* Parse Arguments */
			if (argc >= 2)
			{
				if (!_tcscmp(argv[1], _T("-start")))
				{
					dbglog("Start the UEService", DEBUG_LOG_LEVEL_INFO, logfd, __FILE__, __LINE__);
					nRet = theUEService.Start(strBaseDir);
					if (nRet)
					{
						dbglogex("UEService stopped with return value: %d", DEBUG_LOG_LEVEL_ERRO, logfd, __FILE__, __LINE__, nRet);
					}
					else
					{
						dbglog("UEService stopped", DEBUG_LOG_LEVEL_INFO, logfd, __FILE__, __LINE__);
					}
				} /* -start */
				else if (!_tcscmp(argv[1], _T("-stop")))
				{
					dbglog("Try to stop the running UEService", DEBUG_LOG_LEVEL_INFO, logfd, __FILE__, __LINE__);
					nRet = CUEService::StopService();
					if (nRet)
					{
						dbglogex("Stop UEService failed with return value: %d", DEBUG_LOG_LEVEL_ERRO, logfd, __FILE__, __LINE__, nRet);
						switch (nRet)
						{
						case 1:
							_tprintf(_T("Failed to connect to UEServer, please verify whether the UEServer is running now\r\n"));
							break;
						default:
							_tprintf(_T("Error occur when stop UEServer, please try again later\r\n"));
							break;
						}
					}
					else
					{
						dbglog("Stop the running UEService success", DEBUG_LOG_LEVEL_INFO, logfd, __FILE__, __LINE__);
						_tprintf(_T("Stop UEService success\r\n"));
					}
				} /* -stop */
				else if (!_tcscmp(argv[1], _T("-initdb")))
				{
					dbglog("Create new UE server database", DEBUG_LOG_LEVEL_INFO, logfd, __FILE__, __LINE__);
					if (argc >= 3)
					{
						dbglogex("Database type is: %s", DEBUG_LOG_LEVEL_INFO, logfd, __FILE__, __LINE__, argv[2]);

						if (!_tcscmp(argv[2], _T("cgi")))
						{
							dbglog("Create CGI database...", DEBUG_LOG_LEVEL_INFO, logfd, __FILE__, __LINE__);
							if (CUECGIDatabase::Create(strBaseDir, bDropOldDB))
							{
								dbglog("Create CGI database success", DEBUG_LOG_LEVEL_INFO, logfd, __FILE__, __LINE__);
								nRet = 0;
							}
							else
							{
								dbglog("Create CGI database failed", DEBUG_LOG_LEVEL_ERRO, logfd, __FILE__, __LINE__);
								nRet = 1;
							}
						}
						else
						{
							dbglog("Unknown database to create.", DEBUG_LOG_LEVEL_INFO, logfd, __FILE__, __LINE__);
							display_usage();
						}
					}
					else
					{
						dbglog("No database type specified, create UE back-end server DB", DEBUG_LOG_LEVEL_INFO, logfd, __FILE__, __LINE__);
					}
				} /* -initdb */
				else
				{
					/* Unknown option, display usage */
					display_usage();
				}
			}
			else
			{
				/* No arguments, display usage */
				display_usage();
			}

			/* Terminates use of the Winsock 2 */
			dbglog("Cleanup windows socket", DEBUG_LOG_LEVEL_INFO, logfd, __FILE__, __LINE__);
			WSACleanup();
		} /* Init windows socket success */
		else
		{
			dbglog("Initialize windows socket failed", DEBUG_LOG_LEVEL_ERRO, logfd, __FILE__, __LINE__);
		} /* Init windows socket failed */

		/* Terminates use of the GID+ */
		dbglog("Cleanup windows GDI+", DEBUG_LOG_LEVEL_INFO, logfd, __FILE__, __LINE__);
		Gdiplus::GdiplusShutdown(gdiplusToken);
	} /* Init GDI+ success */
	else
	{
		dbglogex("Init GDI+ failed with return value: %d", DEBUG_LOG_LEVEL_ERRO, logfd, __FILE__, __LINE__, nRet);
	} /* Init GDI+ failed */

	/* Close log file descriptor */
	if (logfd > 0)
		_close(logfd);

	return nRet;
}